package me.cabbit.digitone.tones.gradients;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.FileNotFoundException;

import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import me.cabbit.digitone.support.Strings;
import me.cabbit.digitone.tones.halftone.Halftone;

import org.w3c.dom.Element;

/**
 * A class that implements a dot-based gradient screentone 
 * in a linear fashion. Basically, it creates a grayscale gradient 
 * image as wide as the canvas, then uses the {@link Halftone} 
 * screentone to create the dots
 * 
 * @author Arkaid
 *
 */
public class LinearGradientPanel extends Halftone implements ChangeListener, ItemListener {

	//------------------------------------------------------------------//
	//--------------------------- properties ---------------------------//
	//------------------------------------------------------------------//

	/** Options panel. This gets added to the main window's toolbox */	
	private JPanel panel;

	/** Strings for internationalization */	
	private Strings strings;
	
	/** Min dot size control */	
	private JSpinner spnMinSize;

	/** Max dot size control */	
	private JSpinner spnMaxSize;

	/** Horizontal spacing control */	
	private JSpinner spnHSpacing;

	/** Vertical spacing control */	
	private JSpinner spnVSpacing;

	/** angle control */	
	private JSpinner spnAngle;

	/** Color invert control */	
	private JCheckBox chkInvert;
	
	/** A flag to indicate a preset is being loaded. Used to
	 * temporarily refresh the interface without triggering
	 * events
	 */
	private boolean isLoadingPreset;
	
	//------------------------------------------------------------------//
	//----------------------------- methods ----------------------------//
	//------------------------------------------------------------------//

	/**
	 * Creates the panel and adds all the necessary controls
	 * 
	 * @throws FileNotFoundException if the strings file is not found
	 */
	public LinearGradientPanel() throws FileNotFoundException
	{
		//load strings
		this.strings = new Strings("lang/LinearGradientTone.strings");
		Strings str = this.strings;
	
		//build panel
		this.panel = new JPanel();
		
		//set layout and constraints
		//Set layout and prepare constraints
		this.panel.setLayout(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();
		c.insets = new Insets(3, 3, 3, 3);
		c.fill = GridBagConstraints.HORIZONTAL;

		//min dotsize
		c.gridx = 0; c.gridy = 0; c.weightx = 0.25;
		this.panel.add(new JLabel(str.get("OPTION_MIN_RADIUS")), c);
		
		c.gridx = 1; c.weightx = 0.75;
		this.spnMinSize = new JSpinner(new SpinnerNumberModel(1, 0, null, 1));
		this.spnMinSize.setValue(this.getMinSize());
		this.spnMinSize.addChangeListener(this);
		this.panel.add(this.spnMinSize, c);
		
		//max dotsize
		c.gridx = 0; c.gridy++; c.weightx = 0.25;
		this.panel.add(new JLabel(str.get("OPTION_MAX_RADIUS")), c);
		
		c.gridx = 1; c.weightx = 0.75;
		this.spnMaxSize = new JSpinner(new SpinnerNumberModel(1, 0, null, 1));
		this.spnMaxSize.setValue(this.getMaxSize());
		this.spnMaxSize.addChangeListener(this);
		this.panel.add(this.spnMaxSize, c);		
		
		//horizontal spacing
		c.gridx = 0; c.gridy++; c.weightx = 0.25;
		this.panel.add(new JLabel(str.get("OPTION_HORIZONTAL_SPACING")), c);
		
		c.gridx = 1; c.weightx = 0.75;
		this.spnHSpacing = new JSpinner(new SpinnerNumberModel(0, 0, null, 1));
		this.spnHSpacing.setValue(this.getHorizontalSpacing());
		this.spnHSpacing.addChangeListener(this);
		this.panel.add(this.spnHSpacing, c);
		
		//vertical spacing
		c.gridx = 0; c.gridy++; c.weightx = 0.25;
		this.panel.add(new JLabel(str.get("OPTION_VERTICAL_SPACING")), c);
		
		c.gridx = 1; c.weightx = 0.75;
		this.spnVSpacing = new JSpinner(new SpinnerNumberModel(0, 0, null, 1));
		this.spnVSpacing.setValue(this.getVerticalSpacing());
		this.spnVSpacing.addChangeListener(this);
		this.panel.add(this.spnVSpacing, c);	
		
		//angle
		c.gridx = 0; c.gridy++; c.weightx = 0.25;
		this.panel.add(new JLabel(str.get("OPTION_ANGLE")), c);
		
		c.gridx = 1; c.weightx = 0.75;
		this.spnAngle = new JSpinner(new SpinnerNumberModel(0, 0, 90, 1));
		this.spnAngle.setValue(this.getAngle());
		this.spnAngle.addChangeListener(this);
		this.panel.add(this.spnAngle, c);
		//invert colors
		c.gridx = 0; c.gridy++; c.weightx = 0.25;
		this.panel.add(new JLabel(str.get("OPTION_INVERT_COLORS")), c);
		
		c.gridx = 1; c.weightx = 0.75;
		this.chkInvert = new JCheckBox();
		this.chkInvert.setSelected(this.getInvertColors());
		this.chkInvert.addItemListener(this);
		this.panel.add(this.chkInvert, c);
		
		//empty panel to push everything to the top
		c.gridx = 0; c.gridy++; c.weighty = 1; 
		this.panel.add(new JPanel(), c);

		this.isLoadingPreset = false;
	}
	
	@Override
	public void setSize(int w, int h) {
		super.setSize(w, h);
		
		//create a gradient to fit the new canvas
		BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
		
		//get the pixel array
		byte [] pixels = ((DataBufferByte) img.getRaster().getDataBuffer()).getData();
		
		//how much to change per pixel
		double step = 255.0 / w;

		//for every row
		for (int j = 0; j < h; j++)
		{
			double gray = 0;
			//for every pixel in that row
			for (int i = 0; i < w; i++)
			{
				//set gray value
				pixels[j * w + i] = (byte)gray;
				gray+=step;
			}
		}
		
		setSource(img);
	}
	
	@Override
	public void loadPreset(Element node) {
		//first load the values internally for the screentone
		super.loadPreset(node);
		
		//temporarily turn off updating
		this.isLoadingPreset = true;

		//update interface		
		
		this.spnMinSize.setValue(this.getMinSize());
		this.spnMaxSize.setValue(this.getMaxSize());
		this.spnHSpacing.setValue(this.getHorizontalSpacing());
		this.spnVSpacing.setValue(this.getVerticalSpacing());
		this.spnAngle.setValue(this.getAngle());
		this.chkInvert.setSelected(this.getInvertColors());
		
		//turn it back on
		this.isLoadingPreset = false;
	}
	
	@Override
	public JPanel getPanel() {
		return this.panel;
	}

	@Override
	public String getName() {
		return this.strings.get("SCREENTONE_NAME");
	}

	@Override
	public void stateChanged(ChangeEvent event) {
		
		//do nothing if it's loading from a preset
		if (this.isLoadingPreset)
			return;
			
		//according to who raised the event, update the correct property
		
		//min dot size
		if (event.getSource() == this.spnMinSize)
			this.setMinSize((int)this.spnMinSize.getValue());
		
		//max dot size
		if (event.getSource() == this.spnMaxSize)
			this.setMaxSize((int)this.spnMaxSize.getValue());

		//horizontal spacing
		else if (event.getSource() == this.spnHSpacing) 
			this.setHorizontalSpacing((int)this.spnHSpacing.getValue());
		
		//vertical spacing
		else if (event.getSource() == this.spnVSpacing)
			this.setVerticalSpacing((int)this.spnVSpacing.getValue());

		//angle
		else if (event.getSource() == this.spnAngle)
			this.setAngle((int)this.spnAngle.getValue());
		
		//alert of properties changed (refresh display)
		this.raisePropertyChangedEvent();	
	}

	@Override
	public String getUID() {
		return "88905EA13D6B45AEA99DCD705C2E2653";
	}

	@Override
	public void itemStateChanged(ItemEvent event) {
		//invert colors
		if (event.getSource() == this.chkInvert)
			this.setInvertColors(this.chkInvert.isSelected());

		//alert of properties changed (refresh display)
		this.raisePropertyChangedEvent();	
	}


}
